/**
 * Simple prolog mashine
 * Author: Bobrikovich, Kamenkov
 *
 * About Boost Spirit
 * 
 * carefully using a combination of using namespace & namespace statements
 * if you get with
 *
 * test data: P(X,Y)<-N(Y,X);M(X,X);L(Y,Y);K(X,Y).
 *
 */
#include "interpreter.hpp"
#include "../utils/logger.hpp"
#include "../utils/utils.hpp"

#include <boost/bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>

namespace mashine { namespace input {

using namespace boost::spirit;
using namespace boost::phoenix;

/**
 * Grammar constructor
 */
Interpreter::Grammar::Grammar()
		: qi::grammar<std::string::const_iterator, unsigned(), 
				ascii::space_type>::base_type(allRules, "input") {
	
	//placeholders
	using qi::_1;
	using qi::_2;
	using qi::_3;
	using qi::_4;
	using qi::_val;

	// character parsers
	using qi::char_;
	using qi::alpha;
	using ascii::lower;
	using ascii::upper;

	// for error handling
	using qi::on_error;
	using qi::fail;

	// semantic actions
	action = new boost::phoenix::function<Interpreter::Grammar::Semantic>();

	//
	// Fact parser
	//
	lowerWord 	= 	(+lower) [action->operator()(_val, _1)]
				;
	upperWord 	= 	(+upper) [action->operator()(_val, _1)]
				;
	fact 		= 	(upperWord >> '(' >> lowerWord % ',') 
					[action->operator()(_val, _1, _2)] 
					
				>> 	')' >> '.'
				;


	//
	// Analogy parser
	//
	upperPair 	= 	(upperWord >> ',' >> upperWord)
					[action->operator()(_val, _1, _2)]
				;
	lowerPair 	= 	(lowerWord >> ',' >> lowerWord)
					[action->operator()(_val, _1, _2)]
				;
	analogy 	%= 	(lowerWord >> '(' >> (upperPair | lowerPair))
					[action->operator()(_val, _1, _2)]
					
				>> 	')' >> '.'
				;


	//
	// Rule parser
	//
	predicate 	%= 	(upperWord >> '(' >>
				(
					upperWord | lowerWord
				)
				% ',') [action->operator()(_val, _1, _2)] >> ')'
				;
	canon 		%= 	(predicate >> lit("<-") >> predicate % ';') 
					[action->operator()(_val, _1, _2)] 
					
				>> 	'.'
				;



	//
	// Goal parser
	//
	goal		%=	(predicate % ',') [action->operator()(_val, _1)] >> '.'
				;
			

	//
	// Start rule
	//
	allRules 	%= 	(fact 	 	[action->operator()(_1)]) [_val = 1]
				| 	(analogy 	[action->operator()(_1)]) [_val = 2]
				| 	(canon 		[action->operator()(_1)]) [_val = 3]
				|	(goal		[action->operator()(_1)]) [_val = 4]
				;
	
	// set names to rules
	allRules.name("input");
	predicate.name("predicate");
	analogy.name("analogy");
	canon.name("canon");
	fact.name("fact");

	// error handling
	on_error<fail> (allRules, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);
			
	on_error<fail> (fact, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);

	on_error<fail> (analogy, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);
			
	on_error<fail> (canon, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);
			
	on_error<fail> (goal, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);


	APP_LOGGER(info) << "Grammar instantiated";
}

/**
 * Grammar destructor
 */
Interpreter::Grammar::~Grammar() {
	delete action;
	
	APP_LOGGER(info) << "Grammar destroyed";
}

/**
 * Analogy build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Analogy * & analogy, std::string * name, std::vector<std::string *> * params) const
{
	// APP_LOGGER(info) << "Analogy action. init address " << analogy;

	analogy = new mashine::kb::Analogy(*name, *(params->operator[](0)), *(params->operator[](1)));

	APP_LOGGER(info) << "Analogy build : " << analogy->toString();
}

/**
 * Pair (lower or upper) build
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<std::string *> * & pair, std::string * name1, std::string * name2) const 
{	
	// APP_LOGGER(info) << "Pair action. init address " << pair;

	pair = new std::vector<std::string *>();
	
	pair->push_back(name1);
	pair->push_back(name2);
	
	APP_LOGGER(info) << "Pair build : " << pair->operator[](0) << " " << pair->operator[](1);
}

/**
 * Word (lower or upper) build
 */
void Interpreter::Grammar::Semantic::operator()(std::string * & word, std::vector<char> & chars) const
{
	// APP_LOGGER(info) << "Word action. init address " << word;
	
	word = new std::string(chars.begin(), chars.end());

	// APP_LOGGER(info) << "Word build : " << *word;
}

/**
 * Rule build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Rule * & rule, mashine::kb::Literal * notDenied, std::vector<mashine::kb::Literal *> & denied) const
{
	// APP_LOGGER(info) << "Rule action. init address " << rule << " name=" << notDenied->toString();

	BOOST_FOREACH (mashine::kb::Literal * l, denied)
	{
		l->setDenied(true);
	}
	
	// need to create new vector because
	std::vector<mashine::kb::Literal *> * allLiterals = 
			new std::vector<mashine::kb::Literal *>(denied);	// need copy of vector
			
	allLiterals->push_back(notDenied);
	
	rule = new mashine::kb::Rule(allLiterals);

	// APP_LOGGER(info) << "Rule build : " << rule->toString();
}

/**
 * Fact of Predicate build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Literal * & literal, std::string * name, std::vector<std::string *> & params) const
{
	// APP_LOGGER(debug) << "Analogy action. init address " << literal;

	literal = new mashine::kb::Literal(*name, 
			new std::vector<std::string *>(params),		// need copy of vector
			false);

	// APP_LOGGER(debug) << "Literal build : " << literal->toString();
}

/**
 * Goal build
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<mashine::kb::Literal *> * & goal, std::vector<mashine::kb::Literal *> & goal_) const
{
	goal = new std::vector<mashine::kb::Literal *>(goal_);
}

/**
 *  Inference goal 
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<mashine::kb::Literal *> * const & goal) const
{
	APP_LOGGER(debug) << "Goal to inference " << utils::vector_to_string<mashine::kb::Literal>(*goal);
	
	Interpreter::instance()->getBase()->inference(*goal);
}

/**
 * Add entity 
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Literal * entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity->toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
		
	APP_LOGGER(info) << "Literal added : " <<  entity->toString();
}
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Analogy *  entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity->toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
	
	APP_LOGGER(info) << "Analogy added : " <<  entity->toString();
}
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Rule * entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity->toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
	
	APP_LOGGER(info) << "Rule added : " << entity->toString();
}

Interpreter* Interpreter::inst = NULL; // pay attention to the position of stars

Interpreter::Interpreter() {
	gramm = new Grammar();
	APP_LOGGER(info) << "Interpreter instantiated";
}

Interpreter* Interpreter::instance() {
	if (inst == NULL) {
		inst = new Interpreter();
	}
	return inst;
}

std::string & Interpreter::interprete(std::string const & input) {
	message = "";

	std::string::const_iterator first = input.begin();
	std::string::const_iterator last = input.end();

	APP_LOGGER(info) << "Begin interpreting " << input;

	unsigned entityType;
	
	bool result = qi::phrase_parse(first, last, *gramm, ascii::space, entityType);
	
	if (result) {
		
		return this->message;
	
	} else {
		message = "parsing failed. stopped at: \": ";
		message += std::string(first, last);
		message += "\"\n";
		
		return message;
	}
}

void Interpreter::setBase(kb::KnowledgeBase * kBase) 
{
	this->kBase = kBase;
}

kb::KnowledgeBase* Interpreter::getBase() 
{
	return this->kBase;
}

std::string Interpreter::getLastMessage() 
{
	return (this->message);
}

void Interpreter::setLastMessage(std::string & msg)
{
	
}

Interpreter::~Interpreter() {
	delete gramm;
	APP_LOGGER(info) << "Interpreter ";
}

}}